November 6, 2024
# We're interested in party and knowledge; see how they're coded, which is key for
# interpretations of interactions
nes |> group_by(partyid7) |> summarize(n=n()) |> mutate(pct=100*n/sum(n))# A tibble: 8 × 3
partyid7 n pct
<dbl+lbl> <int> <dbl>
1 1 [StrngDem] 890 20.8
2 2 [WkDem] 560 13.1
3 3 [IndDem] 490 11.5
4 4 [Indep] 579 13.6
5 5 [IndRep] 500 11.7
6 6 [WkRep] 508 11.9
7 7 [StrngRep] 721 16.9
8 NA 23 0.539
# Model without interactions - unconditional effects of party and knowledge
summary(lm(ft_SCOTUS ~ partyid7 + polknow + Female + as.factor(Race3), data=nes))
Call:
lm(formula = ft_SCOTUS ~ partyid7 + polknow + Female + as.factor(Race3),
data = nes)
Residuals:
Min 1Q Median 3Q Max
-61.759 -10.249 0.315 11.797 48.643
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 56.1372 1.2239 45.866 < 2e-16 ***
partyid7 -0.5098 0.1650 -3.089 0.002024 **
polknow 1.0134 0.2375 4.267 2.04e-05 ***
Female 2.5948 0.6859 3.783 0.000158 ***
as.factor(Race3)2 -2.2314 1.2018 -1.857 0.063430 .
as.factor(Race3)3 3.5580 1.1184 3.181 0.001480 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 19.27 on 3260 degrees of freedom
(1005 observations deleted due to missingness)
Multiple R-squared: 0.01643, Adjusted R-squared: 0.01492
F-statistic: 10.89 on 5 and 3260 DF, p-value: 2.072e-10
# Model with interactions - conditional effects of party and knowledge
# First, use "full data" that includes missing data (lm takes it out automatically)
h <- lm(ft_SCOTUS ~ partyid7 + polknow + partyid7*polknow + as.factor(Female) +
as.factor(Race3), data=nes)
summary(h, digits=3)
Call:
lm(formula = ft_SCOTUS ~ partyid7 + polknow + partyid7 * polknow +
as.factor(Female) + as.factor(Race3), data = nes)
Residuals:
Min 1Q Median 3Q Max
-61.862 -10.308 0.445 11.756 49.874
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 58.9914 1.8320 32.200 < 2e-16 ***
partyid7 -1.2471 0.3890 -3.206 0.001358 **
polknow 0.1314 0.4837 0.272 0.785880
as.factor(Female)1 2.5865 0.6855 3.773 0.000164 ***
as.factor(Race3)2 -2.6299 1.2161 -2.163 0.030649 *
as.factor(Race3)3 3.4175 1.1199 3.052 0.002294 **
partyid7:polknow 0.2308 0.1103 2.093 0.036433 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 19.26 on 3259 degrees of freedom
(1005 observations deleted due to missingness)
Multiple R-squared: 0.01775, Adjusted R-squared: 0.01594
F-statistic: 9.814 on 6 and 3259 DF, p-value: 9.525e-11
# Alternative: If you include the multiplicative term, lm automatically
# includes the constituent terms.
summary(lm(ft_SCOTUS ~ partyid7*polknow + as.factor(Female) +
as.factor(Race3), data=nes))
Call:
lm(formula = ft_SCOTUS ~ partyid7 * polknow + as.factor(Female) +
as.factor(Race3), data = nes)
Residuals:
Min 1Q Median 3Q Max
-61.862 -10.308 0.445 11.756 49.874
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 58.9914 1.8320 32.200 < 2e-16 ***
partyid7 -1.2471 0.3890 -3.206 0.001358 **
polknow 0.1314 0.4837 0.272 0.785880
as.factor(Female)1 2.5865 0.6855 3.773 0.000164 ***
as.factor(Race3)2 -2.6299 1.2161 -2.163 0.030649 *
as.factor(Race3)3 3.4175 1.1199 3.052 0.002294 **
partyid7:polknow 0.2308 0.1103 2.093 0.036433 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 19.26 on 3259 degrees of freedom
(1005 observations deleted due to missingness)
Multiple R-squared: 0.01775, Adjusted R-squared: 0.01594
F-statistic: 9.814 on 6 and 3259 DF, p-value: 9.525e-11
# Generate marginal effects of party id conditional on knowledge
# We could do this manually -- write out equation
### Graph marginal effects (Brambor, Clark and Golder graph)
# Use slopes command from marginaleffects package
mepid <- slopes(
h, #model object
variables = "partyid7", #variable for which we want marginal effect
df = insight::get_df(h), #sets df for model to use t dist instead of z
numderiv = "richardson", #SEs, delta method
newdata = datagrid(polknow = 0:5)) #polknow the moderator, set range# Use ggplot, dotted lines for 95% CIs; add horizontal zero line
ggplot(mepid, aes(x = polknow)) +
geom_line(aes(y = estimate)) +
geom_line(aes(y = conf.high), linetype = 2) +
geom_line(aes(y = conf.low), linetype = 2) +
geom_hline(yintercept = 0) +
labs(title="Marginal effect of Party ID", x="Political knowledge",
y="Marginal effect of Party ID")# Use ribbon instead of dotted lines for 95% CI
ggplot(mepid, aes(x=polknow, y=estimate, ymin=conf.low, ymax=conf.high)) +
geom_line(color="black") +
geom_ribbon(alpha=.3, fill="dodgerblue") +
geom_hline(yintercept = 0, linetype="longdash", linewidth=.3) +
labs(title="Marginal effect of Party ID", x="Political knowledge",
y="Marginal effect of Party ID")# Use same concept, but use dots and rspikes
ggplot(mepid, aes(x=polknow, y=estimate, ymin=conf.low, ymax=conf.high)) +
geom_point(color="black", size=2) +
geom_errorbar(width=0) +
theme(plot.title = element_text(face="bold", size=12, hjust = 0.5)) +
geom_hline(yintercept = 0, linetype="dashed") +
theme_minimal() +
labs(title="Marginal Effect of Party ID", x="Political knowledge",
y="Marginal effect of Party ID") +
theme(plot.title = element_text(face="bold", size=12, hjust = 0.5))# Flip to horizontal; typically used when you label the vertical axes with long labels
ggplot(mepid, aes(x=polknow, y=estimate, ymin=conf.low, ymax=conf.high)) +
geom_point(color="black", size=2) +
geom_errorbar(width=0) +
geom_hline(yintercept = 0, linetype="dashed") +
theme_minimal() +
coord_flip() +
scale_x_continuous(breaks=c(0:5),
labels=c('Very low knowledge','Low knowledge','Med-low knowledge',
'Med-high knowledge','High knowledge','Very high knowledge')) +
labs(title="Marginal Effect of Party ID", x=NULL,
y="Marginal effect of Party ID") +
theme(plot.title = element_text(face="bold", size=12, hjust = 0.5))## Second way of graphing interactions:
# Plot partial slopes for the effect of party on SC evals conditional on
# low and high knowledge.
lknow <- predictions(
h,
by="partyid7",
df = insight::get_df(h),
numderiv = "richardson",
newdata = datagrid(
polknow = 0, partyid7=1:7, grid_type="counterfactual"))
hknow <- predictions(
h,
by="partyid7",
numderiv = "richardson",
df = insight::get_df(h),
newdata = datagrid(
polknow = 5, partyid7=1:7, grid_type="counterfactual"))ggplot(data=lknow, aes(x=partyid7, y=estimate)) +
geom_line() +
geom_line(data=hknow, aes(x=partyid7, y=estimate), color="dodgerblue") +
geom_text(x=4, y=62, label="High pol. knowledge", color="dodgerblue") +
geom_text(x=5, y=53, label="Low pol. knowledge", color="black") +
scale_x_continuous(breaks=c(1:7), labels=c('SD','WD','ID','I','IR','WR','SR')) +
scale_y_continuous(limits = c(50, 65), breaks = seq(50, 65, by = 5)) +
theme_minimal() +
labs(title="Conditional Effect of Party ID", x="Party ID",
y="Supreme Court Thermometer") +
theme(plot.title = element_text(face="bold", size=12, hjust = 0.5))